#include <UnitTest++.h>
#include <Math/Random.hpp>
#include <Math/Statistics.hpp>
using namespace zzz;
SUITE(RandTest)
{
  TEST(CommonRandomTest)
  {
    RandomLCG r;
    zuint last=0;
    for (zuint i=0; i<100; i++)
    {
      CHECK(last!=r.Rand());
      last=r.Rand();
    }
    r.Seed(TimeSeed());
    for (zuint i=0; i<100; i++)
    {
      CHECK(last!=r.Rand());
      last=r.Rand();
    }

    RandomLFSRMix r2;
    for (zuint i=0; i<100; i++)
    {
      CHECK(last!=r2.Rand());
      last=r.Rand();
    }
    r.Seed(TimeSeed());
    for (zuint i=0; i<100; i++)
    {
      CHECK(last!=r2.Rand());
      last=r.Rand();
    }

    RandomReal<double> r3(0.0,9.9);
    for (zuint i=0; i<100; i++)
    {
      double x=r3.Rand();
      CHECK(x>=0.0 && x<=9.9);
    }

    RandomInteger<long> r4(54321,987654321);
    for (zuint i=0; i<100; i++)
    {
      long x=r4.Rand();
      CHECK(x>=54321 && x<=987654321);
    }
  }

  TEST(SphereRandomTest)
  {
    RandomSphere<double> r;
    for (zuint i=0; i<100; i++)
    {
      Vector3d x=r.Rand();
      CHECK_CLOSE(1.0,x.Len(),EPSILON);
    }
  }

  TEST(HyperSphereRandomTest)
  {
    //2d
    RandomHyperSphere<2,double> r;
    for (zuint i=0; i<100; i++)
    {
      Vector<2,double> x=r.Rand();
      CHECK_CLOSE(1.0,x.Len(),EPSILON);
    }
    //3d
    RandomHyperSphere<3,double> r2;
    for (zuint i=0; i<100; i++)
    {
      Vector<3,double> x=r2.Rand();
      CHECK_CLOSE(1.0,x.Len(),EPSILON);
    }
    //8d
    RandomHyperSphere<8,double> r3;
    for (zuint i=0; i<100; i++)
    {
      Vector<8,double> x=r3.Rand();
      CHECK_CLOSE(1.0,x.Len(),EPSILON);
    }
    //rand on dim
    for (zuint i=0; i<100; i++)
    {
      Vector<8,double> x=r3.RandOnDim(3);
      CHECK_CLOSE(1.0,x.Len(),EPSILON);
      for (zuint j=3; j<8; j++)
        CHECK_EQUAL(0,x[j]);
    }
  }

  TEST(HyperSphereRandom2Test)
  {
    //2d
    RandomHyperSphere2<2,double> r;
    for (zuint i=0; i<100; i++)
    {
      Vector<2,double> x=r.Rand();
      CHECK_CLOSE(1.0,x.Len(),EPSILON);
    }
    //3d
    RandomHyperSphere2<3,double> r2;
    for (zuint i=0; i<100; i++)
    {
      Vector<3,double> x=r2.Rand();
      CHECK_CLOSE(1.0,x.Len(),EPSILON);
    }
    //8d
    RandomHyperSphere2<8,double> r3;
    for (zuint i=0; i<100; i++)
    {
      Vector<8,double> x=r3.Rand();
      CHECK_CLOSE(1.0,x.Len(),EPSILON);
    }
    //rand on dim
    for (zuint i=0; i<100; i++)
    {
      Vector<8,double> x=r3.RandOnDim(3);
      CHECK_CLOSE(1.0,x.Len(),EPSILON);
      for (zuint j=3; j<8; j++)
        CHECK_EQUAL(0,x[j]);
    }
  }

  TEST(RandStatisticsTest)
  {
    RandomReal<double> r(0.0,1.0);
    vector<double> sample;
    sample.reserve(1000000);
    for(int i=0; i<1000000; i++) sample.push_back(r.Rand());
    CHECK_CLOSE(500000.0/1000001.0,Mean(sample),0.001);
  }

  TEST(RandomNormalTest)
  {
    RandomNormal<double> r(0.0,1.0);
    vector<double> sample;
    sample.reserve(1000000);
    for(int i=0; i<1000000; i++) sample.push_back(r.Rand());
    CHECK_CLOSE(0.0,Mean(sample),0.01);
    CHECK_CLOSE(1.0,Variance(sample),0.01);
  }
}